package server.testing;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Class represents the acting of a simualted single client of the system. Its a
 * own thread, started by the LoadTestingService.
 */
public class LoadTestingClientThread extends Thread {

	private int auctionsPerMin, auctionDuration, updateIntervalSec, bidsPerMin;
	private Timer auctionsCreateTimer = new Timer();
	private Timer getAuctionListTimer = new Timer();
	private Timer bidTimer = new Timer();
	Socket socket = null;
	private PrintWriter clientOut = null;
	private BufferedReader clientIn = null;
	private int clientID;
	private static int threadCounter = 0;
	private static int auctionIDCounter = 0;
	private Random generator = null;
	private static boolean connectionLostNotified = false;

	public LoadTestingClientThread(String host, int tcpPort,
			int auctionsPerMin, int auctionDuration, int updateIntervalSec,
			int bidsPerMin) {
		this.auctionsPerMin = auctionsPerMin;
		this.auctionDuration = auctionDuration;
		this.updateIntervalSec = updateIntervalSec;
		this.bidsPerMin = bidsPerMin;
		synchronized (this) {
			this.clientID = ++threadCounter;
		}
		this.generator = new Random();
		try {
			this.socket = new Socket(host, tcpPort);
			this.clientOut = new PrintWriter(socket.getOutputStream(), true);
			this.clientIn = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
		} catch (IOException e) {
			System.err.println("Couldn't get I/O for the connection to host "
					+ host);
			System.exit(1);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Thread#run()
	 */
	@Override
	public void run() {
		this.clientOut
				.println("!login loadTestingUser" + this.clientID + " -1");
		try {
			if (this.clientIn.readLine().startsWith("Successfully")) {
				long auctionCreateTimerPeriod = (long) ((int) (((60.0 / (double) this.auctionsPerMin) * 1000.0)));
				long auctionCreateTimerDelay = (long) this.generator
						.nextInt((int) auctionCreateTimerPeriod);
				long getAuctionListTimerPeriod = this.updateIntervalSec * 1000;
				long getAuctionListTimerDelay = (long) this.generator
						.nextInt((int) getAuctionListTimerPeriod);
				long bidTimerPeriod = (long) ((int) ((60.0 / (double) this.bidsPerMin) * 1000.0));
				long bidTimerDelay = (long) this.generator
						.nextInt((int) bidTimerPeriod)
						+ auctionCreateTimerDelay;
				this.auctionsCreateTimer.schedule(new AuctionCreateTimerTask(),
						auctionCreateTimerDelay, auctionCreateTimerPeriod);
				this.getAuctionListTimer.schedule(new AuctionListTimerTask(),
						getAuctionListTimerDelay, getAuctionListTimerPeriod);
				this.bidTimer.schedule(new BidTimerTask(), bidTimerDelay,
						bidTimerPeriod);
			} else {
				System.err
						.println("ERROR: Could not log in on server as loadTestingUser "
								+ this.clientID);
			}
		} catch (IOException e) {
			if (!connectionLostNotified) {
				System.err
						.println("FAILURE: Problem with connection to server, press ENTER to stop the service, "
								+ e.getMessage());
				connectionLostNotified = true;
			}
			this.shutDown();
		}
	}

	/**
	 * Shuts down the loadtest, frees all resources.
	 */
	public void shutDown() {
		this.bidTimer.cancel();
		this.getAuctionListTimer.cancel();
		this.auctionsCreateTimer.cancel();
		this.clientOut.println("!end");
		try {
			this.clientIn.close();
			this.clientOut.close();
			// this.socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Task creates new auctions with a duration defined in the Thread class.
	 */
	private class AuctionCreateTimerTask extends TimerTask {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.TimerTask#run()
		 */
		@Override
		public void run() {
			int counter = 0;
			synchronized(this) {
				counter = ++auctionIDCounter;
			}
			clientOut.println("!create " + auctionDuration + " loadTestAuction"
					+ counter);
			try {
				clientIn.readLine();
				// System.out.println(clientIn.readLine());
			} catch (IOException e) {
				if (!connectionLostNotified) {
					System.err
							.println("FAILURE: Problem with connection to server, press ENTER to stop the service, "
									+ e.getMessage());
					connectionLostNotified = true;
				}
				shutDown();
			}
			// System.out.println("AUCTION CREATED");
		}
	}

	/**
	 * Tast lists all active auctions from the server.
	 * 
	 */
	private class AuctionListTimerTask extends TimerTask {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.TimerTask#run()
		 */
		@Override
		public void run() {
			clientOut.println("!list");
			try {
				clientIn.readLine();
			} catch (IOException e) {
				if (!connectionLostNotified) {
					System.err
							.println("FAILURE: Problem with connection to server, press ENTER to stop the service, "
									+ e.getMessage());
					connectionLostNotified = true;
				}
				shutDown();
			}
			// System.out.println("AUCTIONS LISTED");
		}
	}

	/**
	 * Task places a bid on a random auction on the server. New bid is always
	 * the highest.
	 */
	private class BidTimerTask extends TimerTask {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.util.TimerTask#run()
		 */
		@Override
		public void run() {
			clientOut.println("!bid -1 -1");
			try {
				clientIn.readLine();
				// System.out.println(clientIn.readLine());
			} catch (IOException e) {
				if (!connectionLostNotified) {
					System.err
							.println("FAILURE: Problem with connection to server, press ENTER to stop the service, "
									+ e.getMessage());
					connectionLostNotified = true;
				}
				shutDown();
			}

			// System.out.println("BID PLACED");
		}

	}
}
